package YAPclient;

import java.awt.Dimension;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;

import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

import YAPclient.GameInformation.GameMode;
import YAPclient.sound.SoundList.SoundName;
import YAPenvironment.YAPconstant;
import YAPenvironment.YAPerrorCode;
import YAPenvironment.YAPconstant.Command;

/**
 * @author Xaro/Kath/DMaggi/Daniel<br>
 * Receives incoming messages from the server and handles them
 */
public class ParseThread extends Thread{
	
	String sep = YAPconstant.C_SEPARATOR;
	String nl  = YAPconstant.C_NEWLINE;
	String cwc = YAPconstant.C_CHATWHISPERCMD;
	private BufferedReader bufferedReader;
	private PrintWriter printWriter;
		
	@Override
	public void run(){
		
		while(true){
			if(DataModul.running){
				String msg = readMessage();
				if (msg!=null && msg.contains(YAPconstant.C_CMD_SEPARATOR)){
					String[] lst = msg.split(YAPconstant.C_CMD_SEPARATOR);
					for (int i = 0; i < lst.length; i++){
						if ((lst[i].trim() != "") || (lst[i] != null)){
							parseMsg(lst[i]);
						}
					}
				}
			}
			else{
				try {
					sleep(DataModul.sleepTime);
				} catch (InterruptedException e) {
					if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
				}
			}
		}
	}

	
	/*
	 * Can handle whispers and regular chat messages.
	 * Whisper: "/PlayerName Message"
	 * There has to be a space between the PlayerName and the Message
	 */
	public void sendChatMsg(String strMsg){
		strMsg = strMsg.trim().replace(sep, "").replace(YAPconstant.C_CMD_SEPARATOR, "");
		// is it a whisper?
		if (strMsg.charAt(0)==cwc.toCharArray()[0]){
			String name = strMsg.substring(1, strMsg.indexOf(" "));
			String msg = strMsg.substring(strMsg.indexOf(" ")+1).trim();
			if (msg != ""){
				writeMessage(Command.sendMsgTo.toString()+sep+name+sep+msg);
			}
		}
		else{
			writeMessage(Command.sendMsg.toString()+sep+strMsg);
		}
	}
	
	public void initialiceSocket(Socket socket) throws Exception{
		bufferedReader = new BufferedReader( new InputStreamReader( socket.getInputStream()));
		printWriter =  new PrintWriter( new OutputStreamWriter( socket.getOutputStream()));
		
		int i;
		for( i=YAPconstant.C_UDP_SOCKET; i<YAPconstant.C_MAXPORT; i++){
			 try { 
	               DataModul.dSocket = new DatagramSocket(i);
	               break;
	           } catch (Exception e) { 
	        	   //ignore
	           } 
		}
		DataModul.clPlayer.setUdpPort(i);
	}
	
	public String readMessage(){
		String msg = "";
		if (DataModul.sSocket != null){
			try {
		    	msg = bufferedReader.readLine();
			} catch (SocketException e){
				//ignore
			} catch (Exception e) {
				if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
			}
		}
//		if(msg!=null && !msg.startsWith("isAlive")&&  msg!="" && !msg.startsWith("sendP"))
//			System.out.println("Client Input: *"+msg+"*");
	    return msg;
	}
	
	public synchronized void writeMessage(String msg) {
		if (DataModul.sSocket != null){
			msg = msg + YAPconstant.C_CMD_SEPARATOR;
//			if(!msg.startsWith("isAlive"))
//				System.out.println("Client Output: *"+msg+"*");
			printWriter.println(msg);
			printWriter.flush();
		}
	}
	
	private void setLobbyList(String strMsg){
		if(strMsg.startsWith("0") && strMsg.length()>2){
			if (DataModul.flo != null){
				String[] lobbies = strMsg.substring(2,strMsg.length()).split(nl);
			 	DataModul.flo.updateLobbyList(lobbies);
			 }
			 		
       }else if(Integer.parseInt(strMsg)==-2){
    	   DataModul.flo.clearLobbyList();
    	   if(YAPconstant.C_DEBUG)
    		   System.out.println( YAPerrorCode.getErrorMsg(Integer.parseInt(strMsg)) ) ;
		}
		else{
			if(YAPconstant.C_DEBUG)
				System.out.println( YAPerrorCode.getErrorMsg(Integer.parseInt(strMsg)) );
       }
	}
	
	private void setLobbyInformation(String strMsg){
		DataModul.flo.clearPlayersInLobbyList();
		if(strMsg.startsWith("0")){
			strMsg=strMsg.replaceFirst("0"+sep,"");
			String[] strNames=strMsg.split(nl)[0].split(sep);//name is in [0] and [2] and [4]...
			DataModul.flo.updatePlayersInLobbyList(strNames);
			
			DataModul.gInfo.setMaxPlayerCount(Integer.parseInt(strMsg.split(nl)[2].split(sep)[0]));
			DataModul.gInfo.setCurrentPlayerCount(strNames.length/2);
			if (strMsg.split(nl).length >= 2){
				String[] edges=strMsg.split(nl)[1].split(sep);
				DataModul.gInfo.setSides(edges);
			}
			if (strMsg.split(nl).length >= 4){
				String[] sides = strMsg.split(nl)[3].split(sep);
				if (sides.length > 0){
					DataModul.gInfo.setAngle(Integer.parseInt(sides[0]));
				}
				if (sides.length > 1){
					DataModul.gInfo.setSideAssignments(sides);
				}
				DataModul.gInfo.setCurrentLives(Integer.parseInt(strMsg.split(nl)[4]));
			}
			
			/*
			 * From Dario: set the Panels from the servers info given:
			 */
			if(DataModul.fgl!=null && DataModul.fgl.getCreatePanel()!=null){
				if(strMsg.split(nl).length>=4){ 
					DataModul.fgl.getCreatePanel().setLives(Integer.parseInt(strMsg.split(nl)[4]));
				}
				DataModul.fgl.getCreatePanel().setNumberOfWalls(Integer.parseInt(strMsg.split(nl)[2].split(sep)[1])  );
				Double i = new Double(strMsg.split(nl)[2].split(sep)[2]);
				DataModul.fgl.getCreatePanel().setRadSync( (int)(i*10) );
			}
			/*
			 * End of Pfusch
			 */
			DataModul.repaintFrames();
		}
		else{
			int code = Integer.parseInt(strMsg.split(sep)[0]);
			 DataModul.flo.getChatListModel().addElement(YAPerrorCode.getErrorMsg(code));
		}
	}

	public void setPlayGround(String strMsg) {
		String[] strInfo=strMsg.split(nl);
		if (strInfo.length == 4){
			String[] strSliders = strInfo[0].split(sep);
			String[] strBalls = strInfo[1].split(sep);
			String[] strMods = strInfo[2].split(sep);
			String[] strSideState = strInfo[3].split(sep);
			DataModul.gInfo.setBalls(strBalls);
			DataModul.gInfo.setMods(strMods);
			DataModul.gInfo.setSliders(strSliders);
			DataModul.gInfo.setSideState(strSideState);
			DataModul.repaintFrames();
		}
	}
	
	private void setPlayerList(String strMsg){
		if (DataModul.flo != null){
			strMsg.replaceFirst(nl,"");
			String[] players = strMsg.split(nl);
			DataModul.flo.updatePlayersOnServerList(players);
		}
	}
	
	private void createLobby(String strMsg){
		int code;
		if (strMsg.contains(sep)){
			code=Integer.parseInt(strMsg.split(sep)[0]);
		}
		else{
			code = Integer.parseInt(strMsg);
		}
		if (code == 0){
			String lobbyName=strMsg.split(nl)[0].split(sep)[1];
			if (DataModul.flo != null){	
				DataModul.flo.closeCreateLobbyDialog();
				DataModul.flo.setVisible(false);
			}
			DataModul.gInfo.setGameMode(GameInformation.GameMode.lobby);
			DataModul.gInfo.setPlayerIsHost(true);
			DataModul.fgl = new FormGameLobby(lobbyName);
			DataModul.parseThread.writeMessage(Command.getLobbyInformation.toString());
		}else{
			DataModul.gInfo.setMaxPlayerCount(0);
			DataModul.gInfo.setName("");
			DataModul.flo.setErrorMessage(YAPerrorCode.getErrorMsg(code));
			 DataModul.flo.getChatListModel().addElement(YAPerrorCode.getErrorMsg(code));
		}
	}
	
	private void changeName(String strMsg){
		if(Integer.parseInt(strMsg.split(sep)[0])==0){
			String name= strMsg.split(sep)[1];
			DataModul.clPlayer.setName(name);
			DataModul.flo.setTitle("YAP: "+DataModul.clPlayer.getName());
			DataModul.flo.closeChangeNameDialog();
		}else{
			DataModul.flo.setErrorMessage(YAPerrorCode.getErrorMsg(Integer.parseInt(strMsg.split(sep)[0])));
			 DataModul.flo.getChatListModel().addElement(YAPerrorCode.getErrorMsg(Integer.parseInt(strMsg.split(sep)[0])));
		}
	}
	
	private void joinLobby(String strMsg){
		int code= Integer.parseInt(strMsg.split(sep)[0]);
		if (code == 0){
			String title= strMsg.split(sep)[1];
			DataModul.parseThread.writeMessage(Command.getLobbyInformation.toString());
			DataModul.gInfo.setPlayerIsHost(false);
			DataModul.flo.setVisible(false);
			DataModul.fgl = new FormGameLobby(title);
		}else{
			 DataModul.flo.getChatListModel().addElement(YAPerrorCode.getErrorMsg(code));
		}
	}
	
	// incoming chat whispers
	private void setChatMsgFrom(String strMsg){
		String[] data = strMsg.split(sep);
		if ((data.length > 2) && (data[0].equals("0"))){
			if (DataModul.flo != null){
				DataModul.flo.addChatMessage("(from "+data[1]+"): " + data[2]);
			}
		}
		else{
			 DataModul.flo.getChatListModel().addElement(YAPerrorCode.getErrorMsg(Integer.parseInt(data[0])));
		}
	}

	// response from server after whispering someone
	private void setChatMsgTo(String strMsg){
		String[] data = strMsg.split(sep);
		if ((data.length > 2) && (data[0].equals("0"))){
			if (DataModul.flo != null){
				DataModul.flo.addChatMessage("(to "+data[1]+"): " + data[2]);
			}
		}
		else{
			 DataModul.flo.getChatListModel().addElement(YAPerrorCode.getErrorMsg(Integer.parseInt(data[0])));
		}
		
	}
	
	// standard chat messages
	private void setChatMsg(String strMsg){
		if (DataModul.flo != null){
			DataModul.flo.addChatMessage(strMsg);
		}
	}
	
	private void setIsAlive(String strMsg){
		DataModul.isAliveThread.setTimeSent(System.currentTimeMillis());
	}
	
	private void leaveLobby(String strMsg) {
		if(DataModul.udpReceiver!=null)
			DataModul.udpReceiver.close();
		if(strMsg.startsWith("0")){
			DataModul.gInfo.reset();
			DataModul.fgl.resetPGTimer();
			DataModul.gInfo.setGameMode(GameInformation.GameMode.lobbyOverview);
			DataModul.fgl.dispose();
			DataModul.flo.setVisible(true);
			DataModul.flo.clearPlayersInLobbyList();
		}		
	}	
	
	private void pause(String strMsg){
		if( !(strMsg.split(sep).length > 1) ){
			DataModul.flo.getChatListModel().addElement("GAME PAUSED");
			DataModul.gInfo.setIsPaused(true);
		}
		try{
			int code = Integer.parseInt(strMsg.split(sep)[0]);
			if( code == 0){
				DataModul.flo.getChatListModel().addElement("Game PAUSED by: "+strMsg.split(sep)[1]);
				DataModul.gInfo.setIsPaused(true);
				DataModul.clPlayer.pauseSounds();
			}
			else if ( code == 1){
				DataModul.flo.getChatListModel().addElement("Game UNPAUSED by: "+strMsg.split(sep)[1]);
				DataModul.gInfo.setIsPaused(false);
				DataModul.clPlayer.resumeSounds();
			}
		}catch (Exception e){
			if( YAPconstant.C_DEBUG ){ 
				e.printStackTrace(); 
			}
		}
	}
	
	private void sendIsAlive(String strMsg){
		writeMessage(Command.isAliveServer.toString()+sep+"0");
	}
	
	private void startGame(String strMsg) {
		if(DataModul.gInfo.isEndingGame()){
			DataModul.gInfo.interruptEnding();
		}
		String[] split = strMsg.split(YAPconstant.C_SEPARATOR);
		if (split.length > 1){
			if (Integer.parseInt(split[1]) == 0){
				DataModul.gInfo.setStartCounter(0);
				if (DataModul.gInfo != null){
					DataModul.gInfo.setGameMode(GameMode.game);
					
				}
				DataModul.udpReceiver = new UDPReceiverThread();
				DataModul.udpReceiver.start();
			}
			else{
				if (Integer.parseInt(split[1]) == 3)
					DataModul.fgl.switchToGameMode();
				DataModul.gInfo.setStartCounter(Integer.parseInt(split[1]));
				DataModul.repaintFrames();
				if(DataModul.clPlayer.getSound())
					DataModul.clPlayer.playSound(SoundName.startGame);
				}
		}
	}
	
	private void kick(){
		
		DataModul.disconnect();
		System.out.println("Client: disconnected");
		if(DataModul.flo != null)
			DataModul.flo.dispose();
		if(DataModul.fgl != null)
			DataModul.fgl.dispose();
		new YAPgame();
		JOptionPane.showMessageDialog(null,
			    "You were kicked from the server.",
			    "Kicked",
			    JOptionPane.WARNING_MESSAGE);
	}
	
	private void setLives(String strMsg){
		DataModul.gInfo.setCurrentLives(Integer.parseInt(strMsg.split(sep)[2]));
	}
	
	public void parseMsg(final String strMsg){
		try {
			
			SwingUtilities.invokeAndWait(new Runnable(){
				public void run(){
					if(strMsg.equals(""))
						return;
					String[] cmds = strMsg.split(sep);
					String cmd=cmds[0];
					String strMsgP = strMsg.replaceFirst(cmd+sep,"");
					Command command;
					command = Command.valueOf(cmd);

					switch(command){
					case changeName:{
						changeName(strMsgP);
						break;
					}
					case createLobby:{
						createLobby(strMsgP);
						break;
					}
					case getLobbyList:{
						setLobbyList(strMsgP);
						break;
					}
					case getPlayerList:{
						setPlayerList(strMsgP);
						break;
					}
					case getLobbyInformation:{
						setLobbyInformation(strMsgP);
						break;
					}
					case joinLobby:{
						joinLobby(strMsgP);
						break;
					}
					case sendMsg:{
						setChatMsg(strMsgP);
						break;
					}
					case isAliveClient:{
						setIsAlive(strMsgP);
						break;
					}
					case isAliveServer:{
						sendIsAlive(strMsgP);
						break;
					}
					case sendMsgFrom:{
						setChatMsgFrom(strMsgP);
						break;
					}
					case sendMsgTo:{
						setChatMsgTo(strMsgP);
						break;
					}			
					case createPlayField:{
						//nothing to do
						break;
					}
					case setReady:{
						//nothing to do
						break;
					}
					case setUnReady:{
						//nothing to do
						break;
					}
					case leaveLobby:{
						leaveLobby(strMsgP);
						break;
					}
					case pickSide:{
						//nothing to do
						break;
					}
					case startGame:{
						startGame(strMsg);
						break;
					}
					case sendKey:{
						//nothing to do
						break;
					}
					case pause:{
						pause(strMsgP);
						break;
					}
					case leaveGame:{
						//nothing to do
						break;
					}
					case playerLostLife:{
						setLives(strMsg);
						if(DataModul.clPlayer.getSound())
							DataModul.clPlayer.playSound(SoundName.playerLostLife);
						break;
					}
					case sendPowerUpPos:{
						//deprecated
						break;
					}
					case sendPlayGround:{
						//nothing to do (this is handled in the UDPReceiverThread)
						break;
					}
					case getHighScore:{
						showHighScore(strMsgP);
						break;
					}
					case rotate:{
						rotate(strMsg);
						break;
					}
					case sendSound:{
						sendSound(strMsgP);
						break;
					}
					case endGame:{
						endGame(strMsgP);
						break;
					}
					case kick:{
						kick();
					}
					default:{
						if(YAPconstant.C_DEBUG)
							System.out.println("unknown command");
						break;
						}
					}
				}
			});
		}catch(Exception e){
			if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
		}
	}
	
	private void endGame(String strMsg) {
		DataModul.gInfo.setGameMode(GameMode.game);
		if(DataModul.fgl!=null){
			if(DataModul.udpReceiver!=null)
				DataModul.udpReceiver.close();
			String[] message = strMsg.split(sep);
			DataModul.clPlayer.clearSounds();
			DataModul.fgl.resetPGTimer();
			DataModul.fgl.switchToLobbyMode();
			DataModul.fgl.selectUnready();
			DataModul.flo.addChatMessage("~~~");
			for(int i=0; i<message.length; i++){
				if(i == message.length-1){
					DataModul.gInfo.setWinnerName(message[i]);
					DataModul.flo.addChatMessage(message[i] +" "+YAPconstant.getCongratulations());
					DataModul.flo.addChatMessage("~~~");
				}else{
					DataModul.flo.addChatMessage(message[i]);
				}
			}
			DataModul.gInfo.gameEnded();
		}
	}


	protected void rotate(String strMsg) {
		if (DataModul.fgl != null && DataModul.gInfo !=null)
		{
			if (DataModul.fgl.isVisible()){
				if (DataModul.gInfo.getGameMode() == GameMode.game){
					String[] strInfo = strMsg.split(YAPconstant.C_SEPARATOR);
					
					if(strInfo.length > 2){
						//this should be changed to play only if a rotation starts
						DataModul.clPlayer.playSound(SoundName.rotate);
						DataModul.fgl.rotatePlayground(Integer.parseInt(strInfo[1]), Integer.parseInt(strInfo[2]));
					}
				}
			}
		}
		
	}
	
	private void sendSound(String strMsgP) {
		int soundValue = Integer.parseInt(strMsgP);
		SoundName sound = SoundName.values()[soundValue];
		DataModul.clPlayer.playSound(sound);
	}
	
	


	private void showHighScore(String strMsg) {
		if(strMsg==null || strMsg.equals(""))
			return;
		
		strMsg= strMsg.replaceFirst("0"+sep+nl,"");
		
		JDialog hS = new JDialog(DataModul.flo, "High Score:");
		JScrollPane sP= new JScrollPane();
		
		JTextArea hSData= new JTextArea();
		hSData.setEditable(false);
		hSData.setText("***High Scores***");
		String[] data=strMsg.split(YAPconstant.C_NEWLINE);
		for(String i: data)
			hSData.append("\n"+i);
		
		//sP.add(hSData);
		sP.setViewportView(hSData);
		hS.add(sP);
		hS.setSize(new Dimension(200,500));
		hS.setLocationRelativeTo(null);
		hS.setAlwaysOnTop(true);
		hS.setVisible(true);
	}
}
